package com.bw.springboot.starter.upload.service;

import com.bw.springboot.starter.upload.constant.CommonConstant;
import com.bw.springboot.starter.upload.model.Chunk;
import com.bw.springboot.starter.upload.model.Filelist;
import com.bw.springboot.starter.upload.util.FileProperties;
import com.bw.springboot.starter.upload.util.FileResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
 * @author Administrator
 */
@Slf4j
public class FileService {

    private FileProperties properties;

    //构造方法传入属性配置
    public FileService(FileProperties fileProperties) {
        this.properties = fileProperties;
        log.info("FileService 实例化....");
    }

    /**
     * 文件上传
     * @param file
     * @return
     */
    public FileResult upload(MultipartFile file) throws IOException {
        //文件原名
        String originalFilename = file.getOriginalFilename();
        //文件扩展名称
        String extName = originalFilename.substring(originalFilename.lastIndexOf("."),originalFilename.length());
        //保存的文件名称
        String fileName = UUID.randomUUID()+extName;
        //保存文件
        File saveFile = new File(properties.getPath()+fileName);
        file.transferTo(saveFile);
        //返回结果
        FileResult fileResult = new FileResult();
        fileResult.setDomain(properties.getDomain());
        fileResult.setFileName(fileName);
        fileResult.setFileUrl(properties.getDomain()+fileName);
        return fileResult;
    }

    /**
     * 批量上传
     * @param files
     * @return
     * @throws IOException
     */
    public List<FileResult> uploadBatch(MultipartFile[] files) throws IOException {
        List<FileResult> results = new ArrayList<>();
        for (int i = 0; i < files.length; i++) {
            FileResult upload = upload(files[i]);
            results.add(upload);
        }
        return results;
    }

    /**
     * 文件分片上传
     * 更新或新增是否成功 0为失败 1为成功
     * 当要增加的信息已存在时,返回为-1
     * @param chunk
     * @return
     */
    public int postFileUpload(Chunk chunk) {
        MultipartFile file = chunk.getFile();
        log.debug("file originName: {}, chunkNumber: {}", file.getOriginalFilename(), chunk.getChunkNumber());
        //文件上传路径
        String filePath = generatePath(chunk);
        log.info("chunk file path :{}", filePath);
        Path path = Paths.get(filePath);
        //判断文件是否存在
        if (Files.exists(path)) {
            return CommonConstant.UPDATE_EXISTS;
        }
        //上传文件
        try {
            Files.write(path, chunk.getFile().getBytes());
            log.debug("文件 {} 写入成功, md5:{}", chunk.getFilename(), chunk.getIdentifier());
            return CommonConstant.UPDATE_SUCCESS;
        } catch (IOException e) {
            e.printStackTrace();
            return CommonConstant.UPDATE_FAIL;

        }
    }

    /**
     * 合并文件
     * @param chunk
     * @return
     */
    public FileResult mergeFile(Chunk chunk) {
        String filename = chunk.getFilename();
        String file = properties.getPath() + chunk.getIdentifier() + "/" + filename;
        String folder = properties.getPath() + chunk.getIdentifier();
        //文件合并
        merge(file, folder, filename);
        //文件地址
        String path = properties.getDomain()+chunk.getIdentifier() + "/" + filename;
        String domain = chunk.getIdentifier() + "/" + filename;
        return new FileResult().setDomain(properties.getDomain()).setFileUrl(path).setFileName(filename);
    }

    /**
     * 文件合并
     * @param targetFile 要形成的文件名
     * @param folder     要形成的文件夹地址
     * @param filename   文件的名称
     */
    private void merge(String targetFile, String folder, String filename) {
        try {
            Files.createFile(Paths.get(targetFile));
            Files.list(Paths.get(folder))
                    .filter(path -> !path.getFileName().toString().equals(filename))
                    .sorted((o1, o2) -> {
                        String p1 = o1.getFileName().toString();
                        String p2 = o2.getFileName().toString();
                        int i1 = p1.lastIndexOf("-");
                        int i2 = p2.lastIndexOf("-");
                        return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
                    })
                    .forEach(path -> {
                        try {
                            //以追加的形式写入文件
                            Files.write(Paths.get(targetFile), Files.readAllBytes(path), StandardOpenOption.APPEND);
                            //合并后删除该块
                            Files.delete(path);
                        } catch (IOException e) {
                            log.error(e.getMessage(), e);
                        }
                    });
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }



    /**
     * 功能描述:生成块文件所在地址
     */
    private String generatePath(Chunk chunk) {
        //上传目录，读取属性配置
        String uploadFolder = properties.getPath();
        //StringBuilder拼接，效率高
        StringBuilder sb = new StringBuilder();
        //文件夹地址/md5
        sb.append(uploadFolder).append("/").append(chunk.getIdentifier()).append("/");
        //判断uploadFolder/identifier 路径是否存在，不存在则创建
        if (!Files.isWritable(Paths.get(sb.toString()))) {
            log.info("path not exist,create path: {}", sb.toString());
            try {
                Files.createDirectories(Paths.get(sb.toString()));
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        //文件夹地址/md5/文件名-1
        return sb.append(chunk.getFilename())
                .append("-")
                .append(chunk.getChunkNumber()).toString();
    }


}
